package bg.btanev.eightyfour.domain;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bg.btanev.eightyfour.domain.entitybase.DomainEntity;
import bg.btanev.eightyfour.domain.exception.ProfileUpdateException;
import bg.btanev.eightyfour.domain.person.Person;
import bg.btanev.eightyfour.domain.profile.Profile;
import bg.btanev.eightyfour.domain.profile.ProfileEvent;
import bg.btanev.eightyfour.domain.target.TargetStatus;
import bg.btanev.eightyfour.shared.state.ProfileUpdateStatus;
import bg.btanev.eightyfour.shared.state.TargetState;

/**
 *
 * @author B.Tanev
 *
 */
public class Target extends DomainEntity<Long> {
	/**
	 *
	 */
	private static final long serialVersionUID = -6860769218981001691L;

	private static final int DEFAULT_UPDATE_PERIOD = 30;

	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(Target.class);

	private Person person; // one-to-one
	private TargetStatus status; // one-to-one

	// one-to-many; cascade save-update, delete, delete-orphan; inverse=true
	private Set<Profile<? extends ProfileEvent>> profiles = new HashSet<Profile<? extends ProfileEvent>>();

	protected Target() {
		super();
	}

	public Target(Person person) {
		this(null, person, null);
	}

	public Target(Person person, Set<Profile<? extends ProfileEvent>> profiles) {
		this(null, person, profiles);
	}

	public Target(Long id, Person person, Collection<Profile<? extends ProfileEvent>> profiles) {
		super(id);

		Validate.notNull(person, "Person cannot be null.");

		this.person = person;
		this.status = new TargetStatus(this, DEFAULT_UPDATE_PERIOD, TargetState.ACTIVE);

		if (profiles != null) {
			this.addProfiles(profiles);
		}
	}

	public TargetStatus getStatus() {
		return status;
	}

	public Person getPerson() {
		return person;
	}

	public <P extends Profile<? extends ProfileEvent>> boolean addProfile(P profile) {
		logger.debug("addProfile(P) - start"); //$NON-NLS-1$

		Validate.notNull(profile);

		if (!(profile.getTarget().getPerson().getContactInfo().getEmail().equals(getPerson().getContactInfo()
				.getEmail()))) {
			throw new IllegalArgumentException("The profile must have the same target.");
		}

		boolean returnboolean = this.profiles.add(profile);

		logger.debug("addProfile(P) - end"); //$NON-NLS-1$

		return returnboolean;
	}

	public <P extends Profile<? extends ProfileEvent>> boolean addProfiles(Collection<P> profiles) {
		Validate.notNull(profiles);

		boolean result = false;
		for (P p : profiles) {
			result = result | this.addProfile(p);
		}

		return result;
	}

	public <P extends Profile<? extends ProfileEvent>> boolean removeProfile(P profile) {
		Validate.notNull(profile);

		return this.profiles.remove(profile);
	}

	@SuppressWarnings("unchecked")
	public <P extends Profile<? extends ProfileEvent>> Set<P> getProfiles() {
		return (Set<P>) Collections.unmodifiableSet(profiles);
	}

	@SuppressWarnings("unchecked")
	public void updateProfiles() {
		logger.debug("updateProfiles() - start"); //$NON-NLS-1$

		assert (this.profiles != null);

		if (getStatus().getUpdateState() == TargetState.TOPROCESS) {
			getStatus().changeUpdateState(TargetState.PROCESSING);
			if (this.profiles.size() > 0) {
				for (Profile<? extends ProfileEvent> profile : profiles) {
					try {
						List<? extends ProfileEvent> newProfiles = profile.fetchOnlineProfileEvents();
						if (newProfiles != null && newProfiles.size() > 0) {
							profile.updateProfileEvents((List) newProfiles);
						}
						// if newProfile is null no new feed items might be available
						profile.updateStatus(ProfileUpdateStatus.SUCCESSFUL);
					} catch (ProfileUpdateException e) {
						logger.warn("updateProfiles() - exception white updating profile.", e); //$NON-NLS-1$
						profile.updateStatus(ProfileUpdateStatus.FAILED);
					} catch (Exception e) {
						logger.error("updateProfiles() - severe exception white updating profile.", e); //$NON-NLS-1$
					}
				}
			}
			getStatus().changeUpdateState(TargetState.UPDATED);
		} else {
			throw new IllegalStateException(
					"Cannot execute target profiles update process because of currect target state.");
		}

		logger.debug("updateProfiles() - end"); //$NON-NLS-1$
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((person == null) ? 0 : person.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Target other = (Target) obj;
		if (person == null) {
			if (other.person != null)
				return false;
		} else if (!person.equals(other.person))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Target [status=" + status + ", toString()=" + super.toString() + "]";
	}
}
